namespace ScrumSprintMonitor.Samples.FaroTestWatcher.ViewModels
{
	#region #using Directives

	using System;
	using System.Globalization;
	using System.IO;
	using System.Windows.Media;

	using Implementation;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.ServiceLocation;

	#endregion

	internal class TestWatcherViewModel : DataStreamObjectSerializer,
	                                      ITestWatcherViewModel
	{
		#region Construction

		static TestWatcherViewModel()
		{
			var dependsOn = Register<TestWatcherViewModel>();

			dependsOn.Call(me => me.OnPropertyChanged("ShowWarning")).OnChanged(me => me.NumberOfTestsFailed).OnChanged(
				me => me.ReportableData.LastBuild.Status);

			dependsOn.Call(me => me.OnPropertyChanged("BackgroundBrush")).OnChanged(me => me.NumberOfTestsFailed);
		}

		public TestWatcherViewModel(IServiceLocator serviceLocator, IDataStreamProvider streamProvider, ILogger<TestWatcherViewModel> logger,
		                            IShellLayoutConfiguration shellLayoutConfiguration)
			: base(streamProvider)
		{
			this.serviceLocator = serviceLocator;
			this.logger = logger;
			this.shellLayoutConfiguration = shellLayoutConfiguration;

			EnsureObjectIsRead();
		}

		#endregion

		private readonly ILogger<TestWatcherViewModel> logger;
		private readonly IServiceLocator serviceLocator;
		private readonly IShellLayoutConfiguration shellLayoutConfiguration;
		private string buildNumber;
		private DateTime buildTimeStamp;
		private bool disposed;
		private int numberOfTestsFailed;
		private int numberOfTestsRun;
		private IReportableData reportableData;

		#region Overrides of DataStreamObjectSerializer

		protected override void ReadObjectFromStream()
		{
			if (Disposed)
			{
				throw new ObjectDisposedException(GetType().Name);
			}

			try
			{
				using (var stream = this.streamProvider.OpenStream(FileAccess.Read))
				{
					using (var reader = new StreamReader(stream))
					{
						string line = reader.ReadLine();

						if (string.IsNullOrEmpty(line))
						{
							BuildNumber = string.Empty;
							BuildTimeStamp = DateTime.MinValue;
							NumberOfTestsFailed = 0;
							NumberOfTestsRun = 0;
						}
						else
						{
							line = line.TrimStart();

							var tokens = line.Split(new[] {'\t'});
							string buildNumber = tokens[0].Trim();
							string buildTimeStamp = tokens[1].Trim();
							string testsFailed = tokens[2].Trim();
							string testsRun = tokens[3].Trim();

							BuildNumber = buildNumber;
							BuildTimeStamp = DateTime.Parse(buildTimeStamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
							NumberOfTestsFailed = (string.IsNullOrEmpty(testsFailed) ? 0 : int.Parse(testsFailed, CultureInfo.InvariantCulture));
							NumberOfTestsRun = (string.IsNullOrEmpty(testsRun) ? 0 : int.Parse(testsRun, CultureInfo.InvariantCulture));
						}
					}
				}
			}
			catch (Exception e)
			{
				this.logger.Warn(e, "Found exception while trying to load test results file at {0}.", this.streamProvider.Location);

				BuildNumber = string.Empty;
				BuildTimeStamp = DateTime.MinValue;
				NumberOfTestsFailed = 0;
				NumberOfTestsRun = 0;
			}
		}

		#endregion

		#region Private Methods

		private IReportableData ReportableData
		{
			get { return this.reportableData; }
			set
			{
				if (value != this.reportableData)
				{
					this.reportableData = value;

					OnPropertyChanged("ReportableData");
				}
			}
		}

		private IBuildInfo LastBuild
		{
			get
			{
				EnsureConnected();
				return ReportableData.LastBuild;
			}
		}

		private new void GuardDisposed()
		{
			if (this.disposed)
			{
				throw new ObjectDisposedException("FailedBuildRequesterViewModel");
			}
		}

		private void EnsureConnected()
		{
			GuardDisposed();

			if (ReportableData == null)
			{
				ReportableData = this.serviceLocator.GetInstance<IReportableData>();
			}
		}

		#endregion

		#region Implementation of ITestWatcherViewModel

		public bool ShowWarning
		{
			get
			{
				if (this.shellLayoutConfiguration.ActiveSprintMode != SprintMode.Sprinting)
				{
					return false;
				}

				if (LastBuild.Status == BuildStatus.Failed || LastBuild.Status == BuildStatus.PartiallySucceeded)
				{
					return false;
				}

				return true;
			}
		}

		public string BuildNumber
		{
			get
			{
				EnsureObjectIsRead();

				return this.buildNumber;
			}
			private set
			{
				if (value != this.buildNumber)
				{
					this.buildNumber = value;

					AsyncRaisePropertyChanged("BuildNumber");
				}
			}
		}

		public DateTime BuildTimeStamp
		{
			get
			{
				EnsureObjectIsRead();

				return this.buildTimeStamp;
			}
			private set
			{
				if (value != this.buildTimeStamp)
				{
					this.buildTimeStamp = value;

					AsyncRaisePropertyChanged("BuildTimeStamp");
				}
			}
		}

		public int NumberOfTestsFailed
		{
			get
			{
				EnsureObjectIsRead();

				return this.numberOfTestsFailed;
			}
			private set
			{
				if (value != this.numberOfTestsFailed)
				{
					this.numberOfTestsFailed = value;

					AsyncRaisePropertyChanged("NumberOfTestsFailed");
				}
			}
		}

		public int NumberOfTestsRun
		{
			get
			{
				EnsureObjectIsRead();

				return this.numberOfTestsRun;
			}
			private set
			{
				if (value != this.numberOfTestsRun)
				{
					this.numberOfTestsRun = value;

					AsyncRaisePropertyChanged("NumberOfTestsRun");
				}
			}
		}

		public Brush BackgroundBrush
		{
			get { return NumberOfTestsFailed != 0 ? Brushes.Orange : Brushes.Green; }
		}

		#endregion

		protected override void Dispose(bool disposing)
		{
			this.disposed = true;

			SuppressPropertyChanged = true;

			base.Dispose(disposing);

			ReportableData = null;
		}
	}
}